/* $Id: pjsua2_demo.cpp 6026 2019-06-12 06:00:35Z nanang $ */
/*
 * Copyright (C) 2008-2013 Teluu Inc. (http://www.teluu.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <pjsua2.hpp>
#include <iostream>
#include <pj/file_access.h>
#include <stdbool.h>
#include <sys/time.h>

#include "od_engine.h"
#include "od_framework.h"
#include "usecase_sip.h"
#include "od_sip.h"
#include "param_save.h"
#include "sip_param.h"
#include "rpc_response.h"
#include "param_rpc.h"

#define THIS_FILE 	"pjsua2_demo.cpp"

/* report level and macro */
#define RPT_ERR (1) /* error, system error */
#define RPT_WRN (2) /* warning, maybe wrong, maybe OK */
#define RPT_INF (3) /* important information */
#define RPT_DBG (4) /* debug information */

#ifndef S_SPLINT_S /* FIXME */
#define RPTERR(fmt, ...) if(RPT_ERR <= rpt_lvl) fprintf(stderr, "%s: %s: %d: err: " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define RPTWRN(fmt, ...) if(RPT_WRN <= rpt_lvl) fprintf(stderr, "%s: %s: %d: wrn: " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define RPTINF(fmt, ...) if(RPT_INF <= rpt_lvl) fprintf(stderr, "%s: %s: %d: inf: " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
#define RPTDBG(fmt, ...) if(RPT_DBG <= rpt_lvl) fprintf(stderr, "%s: %s: %d: dbg: " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
static int rpt_lvl = RPT_DBG; /* report level: ERR, WRN, INF, DBG */
#else
#define RPTERR(fmt...)
#define RPTWRN(fmt...)
#define RPTINF(fmt...)
#define RPTDBG(fmt...)
#endif


using namespace pj;

class MyAccount;

class MyCall : public Call
{
private:
    MyAccount *myAcc;
    AudioMediaPlayer *wav_player;

public:
    MyCall(Account &acc, int call_id = PJSUA_INVALID_ID)
    : Call(acc, call_id)
    {
    	wav_player = NULL;
        myAcc = (MyAccount *)&acc;
    }
    
    ~MyCall()
    {
    	if (wav_player)
    	    delete wav_player;
    }
    
    virtual void onCallState(OnCallStateParam &prm);
    virtual void onCallTransferRequest(OnCallTransferRequestParam &prm);
    virtual void onCallReplaced(OnCallReplacedParam &prm);
    virtual void onCallMediaState(OnCallMediaStateParam &prm);
    virtual void onCallSdpCreated(OnCallSdpCreatedParam & prm);
    virtual void onStreamCreated(OnStreamCreatedParam & prm);
    virtual void onStreamDestroyed(OnStreamDestroyedParam & prm);
    virtual void onDtmfDigit(OnDtmfDigitParam & prm);
    virtual void onCallTransferStatus(OnCallTransferStatusParam & prm);
    virtual void onCallReplaceRequest(OnCallReplaceRequestParam & prm);
    virtual void onCallRxOffer(OnCallRxOfferParam & prm);
    virtual void onCallRxReinvite(OnCallRxReinviteParam & prm);
    virtual void onCallTxOffer(OnCallTxOfferParam & prm);
    virtual void onCallMediaTransportState(OnCallMediaTransportStateParam & prm);
    virtual void onCallMediaEvent(OnCallMediaEventParam & prm);
    virtual void onCreateMediaTransport(OnCreateMediaTransportParam & prm);
};

class MyAccount : public Account
{
public:
    std::vector<Call *> calls;
    
public:
    MyAccount()
    {}

    ~MyAccount()
    {
        std::cout << "*** Account is being deleted: No of calls="
                  << calls.size() << std::endl;

	for (std::vector<Call *>::iterator it = calls.begin();
             it != calls.end(); )
        {
	    delete (*it);
	    it = calls.erase(it);
        }
    }
    
    void removeCall(Call *call)
    {
        for (std::vector<Call *>::iterator it = calls.begin();
             it != calls.end(); ++it)
        {
            if (*it == call) {
                calls.erase(it);
                break;
            }
        }
    }

    virtual void onRegState(OnRegStateParam &prm)
    {
	AccountInfo ai = getInfo();
	std::cout << (ai.regIsActive? "\r\n*** Register: code=" : "\r\n*** Unregister: code=")
		  << prm.code << std::endl;
    }
    
    virtual void onIncomingCall(OnIncomingCallParam &iprm)
    {
        Call *call = new MyCall(*this, iprm.callId);
        CallInfo ci = call->getInfo();
        CallOpParam prm;
        
        std::cout << "\r\n-------------------------- Incoming Call: " <<  ci.remoteUri << " ["
                  << ci.stateText << "]" << std::endl;
        
        calls.push_back(call);
        prm.statusCode = (pjsip_status_code)200;
        call->answer(prm);
    }
};

void MyCall::onCallState(OnCallStateParam &prm)
{
    PJ_UNUSED_ARG(prm);

    CallInfo ci = getInfo();
    std::cout << "\r\n----------------------- Call: " <<  ci.remoteUri << " [" << ci.stateText
              << "]" << std::endl;
    
    if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
        myAcc->removeCall(this);
        /* Delete the call */
        delete this;
    }
    else if(ci.state == PJSIP_INV_STATE_CONNECTING)
    {
       
    }
}

void MyCall::onCallMediaState(OnCallMediaStateParam &prm)
{
    PJ_UNUSED_ARG(prm);

    unsigned i;
    CallInfo ci = getInfo();
    AudioMedia aud_med;
    AudioMedia& play_dev_med =
    	Endpoint::instance().audDevManager().getPlaybackDevMedia();

    std::cout << "------------------------onCallMediaState, line:" << __LINE__ <<std::endl;

    try {
    	// Get the first audio media
    	aud_med = getAudioMedia(-1);
    } catch(...) {
	std::cout << "-----------------------Failed to get audio media" << std::endl;
	return;
    }

    if (!wav_player) {
    	wav_player = new AudioMediaPlayer();
   	try {
   	    wav_player->createPlayer(
   	    	"../../../../tests/pjsua/wavs/input.16.wav", 0);
   	} catch (...) {
	    std::cout << "------------------------Failed opening wav file" << std::endl;
	    delete wav_player;
	    wav_player = NULL;
    	}
    }

    // This will connect the wav file to the call audio media
    if (wav_player)
    	wav_player->startTransmit(aud_med);

    // And this will connect the call audio media to the sound device/speaker
    aud_med.startTransmit(play_dev_med);
}

void MyCall::onCallTransferRequest(OnCallTransferRequestParam &prm)
{
    std::cout << "------------------------onCallTransferRequest, line:" << __LINE__ <<std::endl;
        
    /* Create new Call for call transfer */
    prm.newCall = new MyCall(*myAcc);
}

void MyCall::onCallReplaced(OnCallReplacedParam &prm)
{
    std::cout << "------------------------onCallReplaced, line:" << __LINE__ <<std::endl;
        
    /* Create new Call for call replace */
    prm.newCall = new MyCall(*myAcc, prm.newCallId);
}

void MyCall::onCallSdpCreated(OnCallSdpCreatedParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallSdpCreate, line:" << __LINE__ <<std::endl;
    std::cout << prm.sdp.wholeSdp <<std::endl;

#if 1
    prm.sdp.wholeSdp = "v=0\r\n"
                        "o=210001 1344933858 1344933858 IN IP4 192.165.53.157\r\n"
                        "s=Play\r\n"
                        "c=IN IP4 192.165.53.157\r\n"
                        "b=AS:2000\r\n"
                        "t=0 0\r\n"
                        "m=audio 35020 RTP/AVP 8\r\n"
                        "a=rtpmap:8 PCMA/8000\r\n"
                        "a=ptime:20\r\n"
                        "a=sendrecv\r\n";
#else


    prm.sdp.wholeSdp =  "v=0\r\n"
                        "o=- 3671502168 3671502168 IN IP4 192.165.53.157\r\n"
                        "s=pjmedia\r\n"
                        "t=0 0\r\n"
                        "a=X-nat:0\r\n"
                        "m=audio 4000 RTP/AVP 96 9 8\r\n"
                        "c=IN IP4 192.165.53.157\r\n"
                        "a=rtcp:4001 IN IP4 192.165.53.157\r\n"
                        "a=sendrecv\r\n"
                        "a=rtpmap:96 telephone-event/8000\r\n"
                        "a=fmtp:96 0-16\r\n"
                        "a=rtpmap:9 G722/8000\r\n"
                        "a=fmtp:9 0-16\r\n"
                        "a=rtpmap:8 PCMA/8000\r\n"
                        "a=ptime:20\r\n"
                        "a=ssrc:690310445 cname:4aa0752c186c1a86";
#endif

}

void MyCall::onStreamCreated(OnStreamCreatedParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onStreamCreated, line:" << __LINE__ <<std::endl;
}

void MyCall::onStreamDestroyed(OnStreamDestroyedParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onStreamDestroyed, line:" << __LINE__ <<std::endl;
}

void MyCall::onDtmfDigit(OnDtmfDigitParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onDtmfDigit, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallTransferStatus(OnCallTransferStatusParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallTransferStatus, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallReplaceRequest(OnCallReplaceRequestParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallReplaceRequest, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallRxOffer(OnCallRxOfferParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallRxOffer, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallRxReinvite(OnCallRxReinviteParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallRxReinvite, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallTxOffer(OnCallTxOfferParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallTxOffer, line:" << __LINE__ <<std::endl;
}

void MyCall::onCallMediaTransportState(OnCallMediaTransportStateParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallMediaTransportState, line:" << __LINE__ <<std::endl;
    std::cout << prm.state  << std::endl;
    std::cout << prm.medIdx << std::endl;
    std::cout << prm.status << std::endl;
}

void MyCall::onCallMediaEvent(OnCallMediaEventParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCallMediaEvent, line:" << __LINE__ <<std::endl;
}

void MyCall::onCreateMediaTransport(OnCreateMediaTransportParam & prm)
{
    PJ_UNUSED_ARG(prm);

    std::cout << "------------------------onCreateMediaTransport, line:" << __LINE__ <<std::endl;
}



static void mainProg1(Endpoint &ep)
{
    // Init library
    EpConfig ep_cfg;
    ep_cfg.logConfig.level = 10;
    ep.libInit( ep_cfg );
    ep.audDevManager().setNoDev();
    //ep.vidDevManager().

    // Transport
    TransportConfig tcfg;
    tcfg.port = 5060;
    ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);

    // Start library
    ep.libStart();
    std::cout << "*** PJSUA2 STARTED ***" << std::endl;

    // Add account
    AccountConfig acc_cfg;
    acc_cfg.idUri = "sip:220@192.165.53.63";

    /*regConfig*/
    SipHeader newHeader;
    newHeader.hName = "Founder";
    newHeader.hValue = "ABCDEFG";
    acc_cfg.regConfig.headers.push_back(newHeader);
    newHeader.hName = "user-agent";
    newHeader.hValue = "suma";
    acc_cfg.regConfig.headers.push_back(newHeader);
    acc_cfg.regConfig.registrarUri = "sip:192.165.53.63";
    acc_cfg.regConfig.timeoutSec = 60;
    acc_cfg.regConfig.retryIntervalSec = 120;
    acc_cfg.regConfig.contactParams = ";my-demo;justfortest";

    /*sipConfig*/
    acc_cfg.sipConfig.authCreds.push_back( AuthCredInfo("digest", "*", "220", 0, "220") );

    /*callConfig*/
    acc_cfg.callConfig.holdType = PJSUA_CALL_HOLD_TYPE_RFC3264;
    acc_cfg.callConfig.prackUse = PJSUA_100REL_OPTIONAL;
    acc_cfg.callConfig.timerUse = PJSUA_SIP_TIMER_REQUIRED;
    acc_cfg.callConfig.timerMinSESec = 90;
    acc_cfg.callConfig.timerSessExpiresSec = 120;
    
    /*MyAccount*/
    MyAccount *acc(new MyAccount);
    try {
	acc->create(acc_cfg);
    } catch (...) {
	std::cout << "Adding account failed" << std::endl;
    }

    //while(1)
    {
        std::cout << "block here!" << std::endl;
        pj_thread_sleep(5000); 
    }        

    // Make outgoing call
    Call *call = new MyCall(*acc);
    acc->calls.push_back(call);   
    CallOpParam prm(true);
    prm.opt.audioCount = 1;
    prm.opt.videoCount = 0; 
    prm.opt.reqKeyframeMethod = PJSUA_VID_REQ_KEYFRAME_SIP_INFO;
    //prm.opt.flag = PJSUA_CALL_INCLUDE_DISABLED_MEDIA;

#if 1
    //prm.txOption.targetUri = "sip:8888@192.165.53.63";
    //SipHeader header;
    //header.hName = "DEMO";
    //header.hValue = "pjsip-demo";
    //prm.txOption.headers.push_back( header );
    prm.txOption.contentType = "application/sdp";
    prm.txOption.msgBody = "v=0\r\n"
                        "o=210001 1344933858 1344933858 IN IP4 192.165.53.157\r\n"
                        "s=Play\r\n"
                        "c=IN IP4 192.165.53.157\r\n"
                        "b=AS:2000\r\n"
                        "t=0 0\r\n"
                        "m=audio 35020 RTP/AVP 97 18 0 8\r\n"
                        "a=rtpmap:97 mpeg4-generic/90000\r\n"
                        "a=ptime:20\r\n"
                        "a=rtpmap:18 G729/8000\r\n"
                        "a=ptime:20\r\n"
                        "a=rtpmap:0 PCMU/8000\r\n"
                        "a=ptime:20\r\n"
                        "a=rtpmap:8 PCMA/8000\r\n"
                        "a=ptime:20\r\n"
                        "a=sendrecv\r\n"
                        "m=video 35018 RTP/AVP 98\r\n"
                        "b=TIAS:2048000\r\n"
                        "a=rtpmap:98 H264/90000\r\n"
                        "a=fmtp:98 profile-level-id=420020;max-mbps=243000;max-fs=8100;\r\n"
                        "a=sendrecv\r\n"
                        "a=content:main\r\n"
                        "m=video 6000 RTP/AVP 98\r\n"
                        "b=TIAS:2048000\r\n"
                        "a=rtpmap:98 H264/90000\r\n"
                        "a=fmtp:98 profile-level-id=420020;max-mbps=243000;max-fs=8100;\r\n"
                        "a=sendrecv\r\n"
                        "a=content:slides\r\n";
#endif 

#if 0
    SipMultipartPart newPart;
    newPart.headers.push_back( header );
    newPart.contentType.type = "application";
    newPart.contentType.subType = "xml";
    newPart.body=  "a test body";

    prm.txOption.multipartParts.push_back( newPart );
    prm.txOption.multipartContentType.type = "application";
    prm.txOption.multipartContentType.subType = "json";
#endif

    try{
        call->makeCall("sip:82345487@192.165.53.63:5060", prm);      
    }catch(Error& err){
        std::cout << err.info() << std::endl;
    }

    while(1)
    {
        std::cout << "block here!"<< __LINE__ << std::endl;
        pj_thread_sleep(5000); 
    }
    
    // Hangup all calls
    pj_thread_sleep(4000);
    ep.hangupAllCalls();
    pj_thread_sleep(4000);
    
    // Destroy library
    std::cout << "*** PJSUA2 SHUTTING DOWN ***" << std::endl;
    delete acc; /* Will delete all calls too */
}

static void mainProg2()
{
    string json_str;
    {
	EpConfig epCfg;
	JsonDocument jDoc;

	epCfg.uaConfig.maxCalls = 61;
	epCfg.uaConfig.userAgent = "Just JSON Test";
	epCfg.uaConfig.stunServer.push_back("stun1.pjsip.org");
	epCfg.uaConfig.stunServer.push_back("stun2.pjsip.org");
	epCfg.logConfig.filename = "THE.LOG";

	jDoc.writeObject(epCfg);
	json_str = jDoc.saveString();
	std::cout << json_str << std::endl << std::endl;
    }

    {
	EpConfig epCfg;
	JsonDocument rDoc;
	string output;

	rDoc.loadString(json_str);
	rDoc.readObject(epCfg);

	JsonDocument wDoc;

	wDoc.writeObject(epCfg);
	json_str = wDoc.saveString();
	std::cout << json_str << std::endl << std::endl;

	wDoc.saveFile("jsontest.js");
    }

    {
	EpConfig epCfg;
	JsonDocument rDoc;

	rDoc.loadFile("jsontest.js");
	rDoc.readObject(epCfg);
	pj_file_delete("jsontest.js");
    }
}


static void mainProg3(Endpoint &ep)
{
    const char *paths[] = { "../../../../tests/pjsua/wavs/input.16.wav",
			    "../../tests/pjsua/wavs/input.16.wav",
			    "input.16.wav"};
    unsigned i;
    const char *filename = NULL;

    // Init library
    EpConfig ep_cfg;
    ep.libInit( ep_cfg );

    for (i=0; i<PJ_ARRAY_SIZE(paths); ++i) {
       if (pj_file_exists(paths[i])) {
          filename = paths[i];
          break;
       }
    }

    if (!filename) {
	PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "mainProg3()",
			   "Could not locate input.16.wav");
    }

    // Start library
    ep.libStart();
    std::cout << "*** PJSUA2 STARTED ***" << std::endl;

    /* Use Null Audio Device as main media clock. This is useful for improving
     * media clock (see also https://trac.pjsip.org/repos/wiki/FAQ#tx-timing)
     * especially when sound device clock is jittery.
     */
    ep.audDevManager().setNullDev();

    /* And install sound device using Extra Audio Device */
    ExtraAudioDevice auddev2(-1, -1);
    try {
	auddev2.open();
    } catch (...) {
	std::cout << "Extra sound device failed" << std::endl;
    }

    // Create player and recorder
    {
	AudioMediaPlayer amp;
	amp.createPlayer(filename);

	AudioMediaRecorder amr;
	amr.createRecorder("recorder_test_output.wav");

	amp.startTransmit(amr);
	if (auddev2.isOpened())
	    amp.startTransmit(auddev2);

	pj_thread_sleep(5000);
    }
}


static void mainProg()
{
    string json_str;

    {
	JsonDocument jdoc;
	AccountConfig accCfg;

	accCfg.idUri = "\"Just Test\" <sip:test@pjsip.org>";
	accCfg.regConfig.registrarUri = "sip:sip.pjsip.org";
	SipHeader h;
	h.hName = "X-Header";
	h.hValue = "User header";
	accCfg.regConfig.headers.push_back(h);

	accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tcp>");
	accCfg.sipConfig.proxies.push_back("<sip:sip.pjsip.org;transport=tls>");

	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(1);
	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(2);
	accCfg.mediaConfig.transportConfig.tlsConfig.ciphers.push_back(3);

	AuthCredInfo aci;
	aci.scheme = "digest";
	aci.username = "test";
	aci.data = "passwd";
	aci.realm = "*";
	accCfg.sipConfig.authCreds.push_back(aci);

	jdoc.writeObject(accCfg);
	json_str = jdoc.saveString();
	std::cout << "Original:" << std::endl;
	std::cout << json_str << std::endl << std::endl;
    }

    {
	JsonDocument rdoc;

	rdoc.loadString(json_str);
	AccountConfig accCfg;
	rdoc.readObject(accCfg);

	JsonDocument wdoc;
	wdoc.writeObject(accCfg);
	json_str = wdoc.saveString();

	std::cout << "Parsed:" << std::endl;
	std::cout << json_str << std::endl << std::endl;
    }
}


static void mainProg4(Endpoint &ep)
{
    // Init library
    EpConfig ep_cfg;
    ep.libInit( ep_cfg );

    // Create transport
    TransportConfig tcfg;
    tcfg.port = 5060;
    ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
    ep.transportCreate(PJSIP_TRANSPORT_TCP, tcfg);

    // Add account
    AccountConfig acc_cfg;
    acc_cfg.idUri = "sip:localhost";
    MyAccount *acc(new MyAccount);
    acc->create(acc_cfg);

    // Start library
    ep.libStart();
    std::cout << "*** PJSUA2 STARTED ***" << std::endl;

    // Just wait for ENTER key
    std::cout << "Press ENTER to quit..." << std::endl;
    std::cin.get();

    delete acc;
}


static void sdp_create()
{
         pj_caching_pool cp;
         pj_pool_t *pool;
         pjmedia_sdp_session *sdp;
         pj_time_val tv;
         pjmedia_sdp_media *m;
         pjmedia_sdp_attr *attr;
         char sdpbuf1[1024] = {0}, sdpbuf2[1024];
         pj_ssize_t len1, len2;
         pj_status_t status;

         /* Must init PJLIB first */
         status = pj_init();
         if (status != PJ_SUCCESS)
     	   return;

         std::cout << "sdp print error!!!\r\n" <<std::endl;         
         pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
         

         pool = pj_pool_create( &cp.factory,     /* pool factory         */
                                "wav",           /* pool name.           */
                                4000,            /* init size            */
                                4000,            /* increment size       */
                                NULL             /* callback on error    */
                                );
         
         sdp = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_session);

         /*build o= line*/
         pj_gettimeofday(&tv);
         sdp->origin.user = pj_str("suma-tech-co.LTD");
         sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
         sdp->origin.net_type = pj_str("IN");
         sdp->origin.addr_type = pj_str("IP4");
         sdp->origin.addr = pj_str("192.165.53.157");
         sdp->name = pj_str("PLAY");

         /* Since we only support one media stream at present, put the
          * SDP connection line in the session level.
          */
         sdp->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
         sdp->conn->net_type = pj_str("IN");
         sdp->conn->addr_type = pj_str("IP4");
         sdp->conn->addr = pj_str("192.165.53.157");;


         /* SDP time and attributes. */
         sdp->time.start = sdp->time.stop = 0;
         sdp->attr_count = 0;

         /* Create media stream 0: */
         sdp->media_count = 1;
         m = (pjmedia_sdp_media *)pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
         sdp->media[0] = m;

         /* Add format and rtpmap for each codec. */
         m->desc.media = pj_str("audio");
         m->desc.port = 19999;
         m->desc.port_count = 1;
         m->desc.transport = pj_str("RTP/AVP");
         m->desc.fmt_count = 1;
         m->attr_count = 0;

         {
             /*rtpmap*/
             pjmedia_sdp_rtpmap rtpmap;
             char ptstr[10];

             sprintf(ptstr, "%d", 8);
             pj_strdup2(pool, &m->desc.fmt[0], ptstr);
             rtpmap.pt = m->desc.fmt[0];
             rtpmap.clock_rate = 9000;
             rtpmap.enc_name = pj_str("G.711A");
             rtpmap.param.slen = 0;

             pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
             m->attr[m->attr_count++] = attr;

             /*fmtp*/
             attr = (pjmedia_sdp_attr *)pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
             attr->name = pj_str("fmtp");
             attr->value = pj_str("102 bitrate=24000");
             m->attr[m->attr_count++] = attr;

             
         }

         len1 = pjmedia_sdp_print(sdp, sdpbuf1, sizeof(sdpbuf1));
         if (len1 < 1) {
             std::cout << "sdp print error!!!\r\n" <<std::endl;
             return;
         }

         std::cout << sdpbuf1 <<std::endl;

}
        
static void cb_test_get_capa(void *h, od_media_config *cfg)
{
    OD_SIP_ASSERT(cfg);
    
    od_media_config local_param = 
    {
        OD_SIP_NET_IPV4,
        OD_SIP_AUD_G711A_8K|OD_SIP_AUD_G711U|OD_SIP_AUD_AAC,
        OD_SIP_VID_H264|OD_SIP_VID_H265,
        5556,
        6664,
        96,
        98,
        101,
        { 1280, 720, 60},    
        { true, 4294967295 },
        256000,
        "192.165.53.157"
    };

    memcpy(cfg, &local_param, sizeof(od_media_config));
                                       
}

static void cb_test_set_cfg(void *h, od_media_params *param)
{
    RPTDBG("call set_cfg!");

    if (param->cfg_type == PARAM_TYPE_ENCODE) {
        RPTWRN("-----encode-params--------------------");
    }else if (param->cfg_type == PARAM_TYPE_DECODE) {
        RPTWRN("-----DECODE-params--------------------");
    }else {
        RPTWRN("-----unknown-params--------------------");
        return;
    }

    RPTWRN("sys_bandw:%d", param->sys_bandw);
    RPTWRN("rtpport_mux:%ld, flg:%d", param->rtpport_mux.muxid, param->rtpport_mux.flg);
    RPTWRN("aud_ip:[%s]", param->aud.aud_ip);
    RPTWRN("enc_name[%s]", param->aud.codec_name);
    RPTWRN("dir[%s]", param->aud.dir);
    RPTWRN("trans_proto[%d]", param->aud.trans_proto);
    RPTWRN("aud_port:%d", param->aud.aud_port);
    RPTWRN("aud_pt:[%d]", param->aud.aud_pt);
    RPTWRN("clock_rate[%d]", param->aud.clock_rate);
    RPTWRN("bitrate[%d]", param->aud.bitrate);

    RPTWRN("vid_ip[%s]", param->vid.vid_ip);
    RPTWRN("enc_name[%s]", param->vid.codec_name);
    RPTWRN("dir[%s]", param->vid.dir);
    RPTWRN("trans_proto:%d", param->vid.trans_proto);
    RPTWRN("vid_port:%d", param->vid.vid_port);
    RPTWRN("vid_pt:%d", param->vid.vid_pt);
    RPTWRN("bitrate:%d", param->vid.bitrate);
    RPTWRN("w:%d", param->vid.w);
    RPTWRN("h:%d", param->vid.h);
    RPTWRN("fps:%d", param->vid.fps);
    RPTWRN("clock_rate:%d", param->vid.clock_rate);
    RPTWRN("profile_level_id:%d", param->vid.profile_level_id);
    RPTWRN("SSRC:%s", param->ssrc);
}

static void cb_test_get_cfg(void *h, od_media_config *cfg)
{
    OD_SIP_ASSERT(cfg);
    
    od_media_config local_param = 
    {
        OD_SIP_NET_IPV4, //net_type
        OD_SIP_AUD_G711A_8K|OD_SIP_AUD_G711U|OD_SIP_AUD_AAC, //audio_codec
        OD_SIP_VID_H264|OD_SIP_VID_H265, //video_codec
        5556, //audio_recv_port
        6664, //video_recv_port
        96,   //vid_h264_pt
        98,   //vid_h265_pt
        102,  //vid_javs_pt
        { 1280, 720, 60},  //reso    
        { true, 4294967295 }, //rtpport_mux
        256000, //vid_bitrate
        "192.165.53.157", //ipaddr
        OD_GB28181_BITRATE_CBR  //bitrate_type
    };

    memcpy(cfg, &local_param, sizeof(od_media_config));
}

static void cb_keyframe(void *h)
{
    RPTWRN("***************keyframe callback working!!");
}

static void cb_ptzctl(void *h, ptzctrl *cmd)
{
    OD_SIP_ASSERT(cmd);

    RPTWRN("%s", cmd->commandname);
    RPTWRN("%s", cmd->operation);
    RPTWRN("%s", cmd->seq);
    RPTWRN("%s", cmd->ts);
    RPTWRN("%s", cmd->devname);
    RPTWRN("%s", cmd->devid);
    RPTWRN("%s,%d", cmd->tilt.direction, cmd->tilt.speed);
    RPTWRN("%s,%d", cmd->pan.direction, cmd->pan.speed);
    RPTWRN("%s,%d", cmd->zoom.zoomctrl, cmd->tilt.speed);
    RPTWRN("%s,%d", cmd->focus.focusctrl, cmd->focus.speed);
    RPTWRN("%s,%d", cmd->aperture.aperturectrl, cmd->aperture.speed);
}

static void cb_ptzposition(void *h, ptzposition *cmd)
{
    OD_SIP_ASSERT(cmd);
    
    RPTWRN("%s", cmd->commandname);
    RPTWRN("%s", cmd->operation);
    RPTWRN("%s", cmd->seq);
    RPTWRN("%s", cmd->ts);
    RPTWRN("%s", cmd->devname);
    RPTWRN("%s", cmd->devid);
    RPTWRN("%s", cmd->pointindexname);
    RPTWRN("%d", cmd->pointindexvalue);
}

static void cb_set_osd(void *h, msg_osd *cmd)
{
    OD_SIP_ASSERT(cmd);
    
    RPTWRN("%s", cmd->Index);
    RPTWRN("%s", cmd->Show);
    RPTWRN("%s", cmd->Left);
    RPTWRN("%s", cmd->Top);
    RPTWRN("%s", cmd->FontName);
    RPTWRN("%s", cmd->FontSize);
    RPTWRN("%s", cmd->Color);
    RPTWRN("%s", cmd->Text);
}

static void cb_set_osds(void *h, msg_osd_multi *cmd)
{
    int idx = 0;
    OD_SIP_ASSERT(cmd);

    for (idx = 0; idx < cmd->array_size; idx++)
    {
        RPTWRN("%d, %d", idx, cmd->array_size);
        RPTWRN("%s", cmd->osd[idx].Index);
        RPTWRN("%s", cmd->osd[idx].Show);
        RPTWRN("%s", cmd->osd[idx].Left);
        RPTWRN("%s", cmd->osd[idx].Top);
        RPTWRN("%s", cmd->osd[idx].FontName);
        RPTWRN("%s", cmd->osd[idx].FontSize);
        RPTWRN("%s", cmd->osd[idx].Color);
        RPTWRN("%s", cmd->osd[idx].Text);
    }
}

static void sdp_create3()
{
        pjmedia_sdp_session *sdp;
        pj_time_val tv;
        pjmedia_sdp_media *m;
        pjmedia_sdp_attr *attr;
        char sdpbuf1[1024] = {0}, sdpbuf2[1024];
        pj_ssize_t len1, len2;
        pj_status_t status;
        od_sip_callback cb;
        od_media_config local_param = 
                                {
                                        OD_SIP_NET_IPV4,

                                        OD_SIP_AUD_G711A_8K|OD_SIP_AUD_G711U|OD_SIP_AUD_AAC,

                                        OD_SIP_VID_H264|OD_SIP_VID_H265,

                                        9998,

                                        7776,

                                        96,

                                        98,

                                        101,

                                        { 1920, 1080, 60},
                                                
                                        { true, 4294967295 },

                                        2048000,

                                        "192.168.1.100"
                                };

        pj_bzero(&cb, sizeof(cb));
        cb.cfg_cb.get_capa = &cb_test_get_capa;
        cb.cfg_cb.get_cfg = &cb_test_get_cfg;
        cb.cfg_cb.set_cfg = &cb_test_set_cfg;
        cb.cfg_cb.h_callback = NULL;
        if (od_sip_comn_init(NULL, &cb) < 0)
            RPTERR("eXosip & pjsip union create error!!!");

        od_sip_ctx *g_ctx = od_sip_lib_start();
        if (!g_ctx)
            RPTERR("get g_ctx error!!!");

        RPTDBG("\r\n--------g_ctx initial success!!!--------");

        od_media_create_initial_sdp(g_ctx,
                                    sdpbuf1,
                                    sizeof(sdpbuf1),
                                    &sdp,
                                    &local_param);
        
        std::cout << sdpbuf1 <<std::endl; 

        /* codec register */
        {
                pj_str_t codec_name;
                
                pj_cstr(&codec_name, "H264");
                status = pjmedia_sdp_neg_register_fmt_match_cb(
                                                        &codec_name,
                                                        &od_codec_h264_match_sdp);
                if (status != PJ_SUCCESS)
                	printf("h264 regiter error!");

                
        }

        {
                od_sip_session_ctx session_ctx;
                pj_bzero(&session_ctx, sizeof(session_ctx));
                
                char *sdp_remote1 =  "v=0\r\n"
                        	    "o=alice 2890844526 2890844526 IN IP4 192.165.58.121\r\n"
                        	    "s=PLAY\r\n"
                        	    "c=IN IP4 192.165.58.121\r\n"
                        	    "t=0 0\r\n"
                        	    "m=audio 49170 RTP/AVP 8\r\n"
                        	    "c=IN IP4 192.168.1.255\r\n"
                        	    "a=rtpmap:8 PCMA/8000\r\n"
                        	    "a=recvonly\r\n"
                        	    "m=video 51372 RTP/AVP 96\r\n"
                        	    "c=IN IP4 192.168.1.255\r\n"
                        	    "b=TIAS:512000\r\n"
                        	    "a=rtpmap:96 H264/90000\r\n"
                        	    "a=fmtp:96 profile-level-id=64002A;max-mbps=243000;max-fs=8100\r\n"
                        	    "a=recvonly\r\n";

               char *sdp_remote2 =      "v=0\r\n"
                                        "o=06770000000 1430622498429749 1 IN IP4 192.168.1.210\r\n"
                                        "s=PLAY\r\n"
                                        "c=IN IP4 10.50.40\r\n"
                                        "t=0 0\r\n"
                                        "m=audio 19999 RTP/AVP 0 108 109 110 111 112 113 114 8 115 116 117 118 119 120 121 100 101 102 103 104 105 106 107\r\n"
                                        "c=IN IP4 52.52.23.23\r\n"
                                        "a=rtpmap:0 PCMU/8000\r\n"
                                        "a=rtpmap:108 PCMU/8000/2\r\n"
                                        "a=rtpmap:109 PCMU/16000\r\n"
                                        "a=rtpmap:110 PCMU/16000/2\r\n"
                                        "a=rtpmap:111 PCMU/32000\r\n"
                                        "a=rtpmap:112 PCMU/32000/2\r\n"
                                        "a=rtpmap:113 PCMU/44100\r\n"
                                        "a=rtpmap:114 PCMU/44100/2\r\n"
                                        "a=rtpmap:8 PCMA/8000\r\n"
                                        "a=rtpmap:115 PCMA/8000/2\r\n"
                                        "a=rtpmap:116 PCMA/16000\r\n"
                                        "a=rtpmap:117 PCMA/16000/2\r\n"
                                        "a=rtpmap:118 PCMA/32000\r\n"
                                        "a=rtpmap:119 PCMA/32000/2\r\n"
                                        "a=rtpmap:120 PCMA/44100\r\n"
                                        "a=rtpmap:121 PCMA/44100/2\r\n"
                                        "a=rtpmap:100 MPEG4-GENERIC/8000\r\n"
                                        "a=fmtp:100 bitrate=16000\r\n"
                                        "a=rtpmap:101 MPEG4-GENERIC/8000/2\r\n"
                                        "a=fmtp:101 bitrate=16000\r\n"
                                        "a=rtpmap:102 MPEG4-GENERIC/16000\r\n"
                                        "a=fmtp:102 bitrate=24000\r\n"
                                        "a=rtpmap:103 MPEG4-GENERIC/16000/2\r\n"
                                        "a=fmtp:103 bitrate=24000\r\n"
                                        "a=rtpmap:104 MPEG4-GENERIC/32000\r\n"
                                        "a=fmtp:104 bitrate=40000\r\n"
                                        "a=rtpmap:105 MPEG4-GENERIC/32000/2\r\n"
                                        "a=fmtp:105 bitrate=40000\r\n"
                                        "a=rtpmap:106 MPEG4-GENERIC/44100\r\n"
                                        "a=fmtp:106 bitrate=80000\r\n"
                                        "a=rtpmap:107 MPEG4-GENERIC/44100/2\r\n"
                                        "a=fmtp:107 bitrate=80000\r\n"
                                        "a=control:track2\r\n"
                                        "a=ssrc:1801398640 cname:rtp_24310144@0\r\n"
                                        "a=recvonly\r\n"
                                        "m=video 19997 RTP/AVP 98 100\r\n"
                                        "c=IN IP4 90.95.90.28\r\n"
                                        "b=TIAS:2048000\r\n"
                                        "a=rtpmap:98 H264/90000\r\n"
                                        "a=rtpmap:100 H265/90000\r\n"
                                        "a=fmtp:98 profile-level-id=640020;max-mbps=39600;max-fs=1584;packetization-mode=1\r\n"
                                        "a=control:track1\r\n"
                                        "a=rtcp-fb:* ccm fir\r\n"
                                        "a=ssrc:3376455274 cname:rtp_24310144@0\r\n"
                                        "a=recvonly\r\n";
                
                session_ctx.session_sdp_neg.session_type = OD_REMOTE_OFFER;
                session_ctx.session_sdp_neg.local_sdp = sdp;
                session_ctx.local_capa_config = &local_param;
                                    
                od_media_sdp_negotiate(g_ctx, 
                                       sdp_remote2, 
                                       strlen(sdp_remote2), 
                                       &session_ctx);

                {
                    od_media_params *param = &session_ctx.decode_media;
                    
                    RPTWRN("-----decode-params--------------------");
                    RPTWRN("sys_bandw:%d", param->sys_bandw);
                    
                    RPTWRN("rtpport_mux:0x%x, flg:%d", param->rtpport_mux.muxid, param->rtpport_mux.flg);
                    
                    RPTWRN("aud_ip:[%s]", param->aud.aud_ip);
                    RPTWRN("enc_name[%s]", param->aud.codec_name);
                    RPTWRN("dir[%s]", param->aud.dir);
                    RPTWRN("trans_proto[%d]", param->aud.trans_proto);
                    RPTWRN("aud_port:%d", param->aud.aud_port);
                    RPTWRN("aud_pt:[%d]", param->aud.aud_pt);
                    RPTWRN("bandwidth[%d]", param->aud.bitrate);
                    RPTWRN("clock_rate[%d]", param->aud.clock_rate);
                    RPTWRN("bitrate[%d]", param->aud.bitrate);

                    RPTWRN("vid_ip[%s]", param->vid.vid_ip);
                    RPTWRN("enc_name[%s]", param->vid.codec_name);
                    RPTWRN("dir[%s]", param->vid.dir);
                    RPTWRN("trans_proto:%d", param->vid.trans_proto);
                    RPTWRN("vid_port:%d", param->vid.vid_port);
                    RPTWRN("vid_pt:%d", param->vid.vid_pt);
                    RPTWRN("bandwidth:%d", param->vid.bitrate);
                    RPTWRN("w:%d", param->vid.w);
                    RPTWRN("h:%d", param->vid.h);
                    RPTWRN("fps:%d", param->vid.fps);
                    RPTWRN("clock_rate:%d", param->vid.clock_rate);
                    RPTWRN("profile_level_id:%d", param->vid.profile_level_id);
                }
            
                {
                    od_media_params *param = &session_ctx.encode_media;

                    RPTWRN("-----encode-params--------------------");
                    RPTWRN("sys_bandw:%d", param->sys_bandw);
                    
                    RPTWRN("rtpport_mux:0x%x, flg:%d", param->rtpport_mux.muxid, param->rtpport_mux.flg);
                    
                    RPTWRN("aud_ip:[%s]", param->aud.aud_ip);
                    RPTWRN("enc_name[%s]", param->aud.codec_name);
                    RPTWRN("dir[%s]", param->aud.dir);
                    RPTWRN("trans_proto[%d]", param->aud.trans_proto);
                    RPTWRN("aud_port:%d", param->aud.aud_port);
                    RPTWRN("aud_pt:[%d]", param->aud.aud_pt);
                    RPTWRN("clock_rate[%d]", param->aud.clock_rate);
                    RPTWRN("bitrate[%d]", param->aud.bitrate);

                    RPTWRN("vid_ip[%s]", param->vid.vid_ip);
                    RPTWRN("enc_name[%s]", param->vid.codec_name);
                    RPTWRN("dir[%s]", param->vid.dir);
                    RPTWRN("trans_proto:%d", param->vid.trans_proto);
                    RPTWRN("vid_port:%d", param->vid.vid_port);
                    RPTWRN("vid_pt:%d", param->vid.vid_pt);
                    RPTWRN("bandwidth:%d", param->vid.bitrate);
                    RPTWRN("w:%d", param->vid.w);
                    RPTWRN("h:%d", param->vid.h);
                    RPTWRN("fps:%d", param->vid.fps);
                    RPTWRN("clock_rate:%d", param->vid.clock_rate);
                    RPTWRN("profile_level_id:%d", param->vid.profile_level_id);
                }
                
        }
        
        pj_pool_reset(g_ctx->pool);

        od_sip_lib_stop();
        od_sip_comn_deinit();
}


#define TASK_HASH_TABLE_SIZE 1024
#define SIP_MSG_TIMEOUT 1000

/*default MQ TRANSFER NAME*/
#define DEF_MQ_TRANS_NAME  "demo_sip_trans"
#define DEMO_ARGC 13

typedef struct
{
    uint32_t src_id;
    uint32_t dst_id;
    uint32_t rpc_id;
    uint32_t notify_recv_id;
    char file_name[OD_MAX_PATH_LEN];
    char post_name[OD_MAX_PATH_LEN];
} sip_startup_params;

static int show_help()
{
    fprintf(stdout,
        "sip.out:\n"
        "\n"
        "Usage: ./demo_od_sip.out [OPTION]\n"
        "\n"
        "Options:\n"
        "    -s,              src od_msg id\n"
        "    -f,              param file name include path\n"
        "    -p,              post transfer name\n"
        "    -h,              print this info \n"
        "\n"
        "Examples:\n"
        "    ./sip.out -s 450 -d 500 -n 511 -r 451 -f ./profile/sip.json -p tx1_mq_trans\n"
        "\n");

    return OD_OK;
}

static int deal_with_parameter(int argc, char *argv[], sip_startup_params *startup_params)
{
    int i;

    strncpy(startup_params->post_name, DEF_MQ_TRANS_NAME, OD_MAX_PATH_LEN - 1);
    if(argc < DEMO_ARGC)
    {
        return OD_ERROR;
    }

    for(i = 1;i < argc;i++)
    {
        if('-' == argv[i][0])
        {
            if(0 == strcmp(argv[i], "-s") || 0 == strcmp(argv[i], "--src"))
            {
                if(i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                startup_params->src_id = atoi(argv[i]);
            }
            else if (0 == strcmp(argv[i], "-d") || 0 == strcmp(argv[i], "--dst"))
            {
                if (i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                startup_params->dst_id = atoi(argv[i]);
            }     
            else if (0 == strcmp(argv[i], "-n") || 0 == strcmp(argv[i], "--notify"))
            {
                if (i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                startup_params->notify_recv_id = atoi(argv[i]);
            }   
            else if (0 == strcmp(argv[i], "-r") || 0 == strcmp(argv[i], "--rpc"))
            {
                if (i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                startup_params->rpc_id = atoi(argv[i]);
            }
            else if(0 == strcmp(argv[i], "-f") || 0 == strcmp(argv[i], "--file"))
            {
                if(i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                strncpy(startup_params->file_name, argv[i], OD_MAX_PATH_LEN - 1);
            }            
            else if(0 == strcmp(argv[i], "-p") || 0 == strcmp(argv[i], "--post"))
            {
                if(i == argc-1)
                {
                    show_help();
                    return OD_ERROR;
                }
                i++;
                strncpy(startup_params->post_name, argv[i], OD_MAX_PATH_LEN - 1);
            }
            else
            {
                RPTERR("Wrong parameter:%c:%s", argv[i][0], argv[i]);
                show_help();
                return OD_ERROR;
            }
        }
        else
        {
            show_help();
            RPTERR("Wrong parameter: %s", argv[i]);
            return OD_ERROR;
        }
    }

    if(argc > DEMO_ARGC)
    {
        show_help();
        RPTERR("Wrong parameter nums : %d\n", argc - 1);
        return OD_ERROR;
    }

    return OD_OK;
}

param_save_list save_tab[] = {
    {NULL, 0, 0, NULL, NULL},
};
    
static void init_param_save_table(param_save_list s_list[], sip_params_obj *sip_handle)
{
    //只需初始化handle和filename，其他在param_save_create时初始化
    s_list[0].save_handle = sip_handle->sip_json_hdl;
    s_list[0].save_filename = sip_handle->sip_json_filename;    
}

#define MAX_JSTR_LEN                (4096)
#define MAX_ERRMSG_NUM              (128)
static char err_table[MAX_ERRMSG_NUM][MAX_JSTR_LEN];

static void save_to_file(rpc_response_dft_hdl *handle)
{
    param_save_list *save_list = handle;
    struct timeval tv = {0};
    uint64_t time;

    RPTWRN("Params set, will save to file \"%s\" in 1s.", handle->save_filename);
    if (NULL == handle || NULL == save_list->mux)
    {
        //参数保存线程未创建则不保存参数
        return;
    }
    
    od_mutex_lock(&(save_list->mux));
    save_list->modify_flag = 1;
    gettimeofday(&tv, NULL);
    time = tv.tv_sec * (uint64_t)1000 + (uint64_t)(tv.tv_usec / 1000);
    save_list->last_modify_time = time;
    od_mutex_unlock(&(save_list->mux));

    return;
}

static response_rpc rpc_response_default_get(void *h, jsonrpc_request_t *request)
{
    struct json_object * jparam_obj_out = NULL;
    struct json_object * jobj_rpc_out = NULL;
    rpc_response_dft_hdl *handle = (rpc_response_dft_hdl *)h;
        
    if (NULL == handle || NULL == request)
    {
        return NULL;
    }
    
    jparam_obj_out = jsparam_get(handle->save_handle, request->jobj_params);
    if (!jparam_obj_out)
    {
        return NULL;
    }
    
    jobj_rpc_out = jsonrpc_response_pack(jparam_obj_out, request->id);

    return jobj_rpc_out;
}

static response_rpc rpc_response_default_set(void *h, jsonrpc_request_t *request)
{
    int len, i, jpos;
    char * temp;
    struct json_object * error_obj = NULL;
    struct json_object * jobj_rpc_out = NULL;
    rpc_response_dft_hdl *handle = (rpc_response_dft_hdl *)h;

    if (NULL == handle || NULL == request)
    {
        return NULL;
    }
    
    if (jsparam_set(handle->save_handle, request->jobj_params) < 0)
    {
        len = 0;
        for (i = 0;i < MAX_ERRMSG_NUM && strlen(err_table[i]) > 0;i++)
        {
            len += strlen(err_table[i]);
        }
        len += MAX_ERRMSG_NUM * 2;//预留给error message换行符使用
        temp = (char *)od_calloc(len, sizeof(char));
        if (!temp)
        {
            return NULL;
        }
    
        //从错误表中提取字符串到error message
        jpos = 0;
        for (i = 0;i < MAX_ERRMSG_NUM && strlen(err_table[i]) > 0;i++)
        {
            strcpy(temp + jpos, err_table[i]);
            jpos += strlen(err_table[i]);
            strcpy(temp + jpos, "\n");
            jpos++;
        }
        RPTDBG("Assembled error message is: %s", temp);
        //错误表先清空，后面即使遇到问题，没有发出错误消息，也不要保留
        memset(err_table, 0, MAX_ERRMSG_NUM * MAX_JSTR_LEN);
    
        error_obj = jsonrpc_make_error_jobj(EUSER_DEFINED, temp);
        if (!error_obj)
        {
            od_free(temp);//消息发不出去了，所以可以free掉
            return NULL;
        }
        
        jobj_rpc_out = jsonrpc_response_pack_error(error_obj, request->id);
        if (!jobj_rpc_out)
        {
            od_free(temp);//消息发不出去了，所以可以free掉
            return NULL;
        }

        //在jsonrpc_make_error_jobj函数内部，用temp生成了一个副本并add到了error_obj上，所以这里可以free
        od_free(temp);
    }
    else
    {
        jobj_rpc_out = jsonrpc_response_pack(json_object_new_string("ok"), request->id);
        //保存参数
        save_to_file(handle);
    }

    return jobj_rpc_out;
}



#define TAB_END {NULL, NULL, NULL}

rpc_response_tab_desc sip_rpc_response_tab[] = {
    {"sip.set", NULL, &rpc_response_default_set},
    {"sip.get", NULL, &rpc_response_default_get},
    TAB_END
};
    
static void sip_rpc_response_tab_init(rpc_response_tab_desc sip_rpc_response_tab[], param_save_list save_tab[])
{
    sip_rpc_response_tab[0].handle = sip_rpc_response_tab[1].handle = &save_tab[0];
    return;
}

static void env_init()
{
   od_init();
   odin_trace_init(NULL, NULL);
}

static int sip_app_demo(int argc, char *argv[])
{
    od_framework_t * frameworks = NULL;
    sip_params_obj * sip_handle = NULL;
    param_save_static_params_t param_save_statics = glb_param_save_static_params_default;
    param_save_handle param_save_hdl = NULL;
    rpc_response_static_params_t rpc_response_statics = glb_rpc_response_static_params_default;
    rpc_response_task_handle rpc_response_hdl = NULL;
    sip_startup_params startup_params;
    od_sip_callback cb;
    int ret;
    od_sip_engine_cfg cfg;
    
    env_init();
    memset(&startup_params, 0, sizeof(sip_startup_params));
    if(deal_with_parameter(argc, argv, &startup_params) != 0) {
        show_help();
        return OD_ERROR;
    } 

    pj_bzero(&cb, sizeof(od_sip_callback));
    cb.cfg_cb.get_capa = &cb_test_get_capa;
    cb.cfg_cb.get_cfg = &cb_test_get_cfg;
    cb.cfg_cb.set_cfg = &cb_test_set_cfg;
    cb.cfg_cb.h_callback = NULL;
    cb.log_cb.call_log = NULL;
    cb.keyframe_cb.keyframe = &cb_keyframe;
    cb.keyframe_cb.h_keyframe = NULL;
    cb.ptzctl_cb.ptzctl = &cb_ptzctl;
    cb.ptzctl_cb.h_ptzctl = NULL;
    cb.ptzctl_cb.ptzpos = &cb_ptzposition;
    cb.ptzctl_cb.h_ptzpos = NULL;
    cb.osd_cb.osd = &cb_set_osd;
    cb.osd_cb.h_osd = NULL;
    cb.osd_cb.osds = &cb_set_osds;
    cb.osd_cb.h_osds = NULL;


    cfg.transport = IPPROTO_UDP;
    cfg.family = AF_INET;
    cfg.local_sip_port = 5060;
    cfg.timer.T1 = 500;
    cfg.timer.T2 = 4000;
    cfg.timer.T4 = 5000;
    cfg.trace_log_lvl = 6;
    cfg.protocol_standard = OD_SIP_PROTOCOL_GB28181_2016;

    
    if (od_sip_comn_init(&cfg, &cb) != OD_OK) {
        RPTERR("eXosip & pjsip union create error!!!");
        return OD_ERROR;
    }

    /*创建framework*/
    frameworks = od_framework_create(sizeof(sip_params_obj), TASK_HASH_TABLE_SIZE);    
    sip_handle = (sip_params_obj *)frameworks->param_table;
    OD_SIP_ASSERT_RETURN(sip_handle, OD_ERROR);
    
    ret = sip_params_init(frameworks, startup_params.file_name);    
    if(0 != ret) {
        RPTERR("sip_params_init error");
        return OD_ERROR;
    }
    
    /*参数表初始化，读取json存储文件*/
    sip_handle->strup_src_id = startup_params.src_id;
    sip_handle->strup_dst_id = startup_params.dst_id;
    sip_handle->strup_rpc_id = startup_params.rpc_id;
    sip_handle->sip_msg_handle = param_rpc_create(startup_params.post_name, startup_params.rpc_id, startup_params.dst_id, SIP_MSG_TIMEOUT);
    if(NULL == sip_handle->sip_msg_handle) {
        RPTERR("SIP param mq create error");
        return OD_ERROR;
    }


    /*启动需要启动的线程任务*/
    ret = sip_module_start(frameworks);
    if (ret < 0 ) {
        RPTERR("sip_module start error!!!");
        return ret;
    }

    /*初始化和启动参数保存线程*/
    param_save_statics.list_num = sizeof(save_tab)/sizeof(param_save_list);
    init_param_save_table(save_tab, (sip_params_obj *)sip_handle);
    param_save_statics.s_list = save_tab;
    param_save_statics.save_func = NULL;
    param_save_hdl = param_save_create(&param_save_statics, NULL);
    od_task_start(param_save_hdl, 60);

    /*启动参数接收处理任务，可与其他任务交互*/
    sip_rpc_response_tab_init(sip_rpc_response_tab, save_tab);
    rpc_response_statics.msg_local_id = startup_params.src_id;
    rpc_response_statics.table = sip_rpc_response_tab;
    strncpy(rpc_response_statics.post_name, startup_params.post_name, OD_MAX_OBJ_NAME - 1);
    rpc_response_hdl = rpc_response_create(&rpc_response_statics, NULL);
    od_task_start(rpc_response_hdl, 60);

    /*等待退出*/
    /*如果想要结束进程，设置exit变量，在其act响应函数中操作done，控制进程结束*/
    while (OD_OFF == sip_handle->sip_params.done)
    {
        RPTDBG("main loop run~");
        od_msleep(10000);
    }
    
    od_task_stop(param_save_hdl);
    od_task_delete(param_save_hdl);

    od_task_stop(rpc_response_hdl);
    od_task_delete(rpc_response_hdl);

    od_sip_comn_deinit();
    sip_module_stop(frameworks);
    od_framework_destroy(frameworks);
}


int main(int argc, char *argv[])
{
    int ret = 0;
#if 0
    
    Endpoint ep;

    try {
	ep.libCreate();

	//mainProg1(ep);

        sdp_create();
	ret = PJ_SUCCESS;
    } catch (Error & err) {
	std::cout << "Exception: " << err.info() << std::endl;
	ret = 1;
    }

    try {
	ep.libDestroy();
    } catch(Error &err) {
	std::cout << "Exception: " << err.info() << std::endl;
	ret = 1;
    }

    if (ret == PJ_SUCCESS) {
	std::cout << "Success" << std::endl;
    } else {
	std::cout << "Error Found" << std::endl;
    }
#else
    //sdp_create();

    //sdp_create2();

    //sdp_create3();    
    sip_app_demo(argc, argv);

#endif
    return ret;
   
}


